#include <linux/sched.h>
#include <linux/sys.h>
#include <linux/ptrace.h>
#include <linux/segment.h>

#include <asm/io.h>

#include <linux/timex.h>

struct timeval xtime; /* The current time */

int need_resched = 0;

extern int timer_interrupt(void);
int system_call(void);

static unsigned long init_kernel_stack[1024];
struct task_struct init_task = INIT_TASK;

unsigned long volatile jiffies = 0;

struct task_struct *current = &init_task;

struct task_struct *task[NR_TASKS] = {
    &init_task,
};

long user_stack[PAGE_SIZE >> 2];

struct {
    long *a;
    short b;
} stack_start = {&user_stack[PAGE_SIZE >> 2], KERNEL_DS};

fn_ptr sys_call_table[] = {sys_setup, sys_exit, sys_fork, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sys_idle};

int NR_syscalls = sizeof(sys_call_table) / sizeof(fn_ptr);

void schedule(void) {
    int c;
    struct task_struct *p;
    struct task_struct *next;

    need_resched = 0;
    p = &init_task;
    for (;;) {
        if ((p = p->next_task) == &init_task)
            goto confuse_gcc1;
        if (p->state != TASK_INTERRUPTIBLE)
            continue;
        if (p->signal & ~p->blocked) {
            p->state = TASK_RUNNING;
            continue;
        }
    }
confuse_gcc1:

    c = -1;
    next = p = &init_task;
    for (;;) {
        p = p->next_task;
        if (p == &init_task)
            goto confuse_gcc2;
        if (p->state == TASK_RUNNING && p->counter > c)
            c = p->counter, next = p;
    }
confuse_gcc2:
    if (!c) {
        for_each_task(p)
            p->counter = (p->counter >> 1) + p->priority;
    }
    switch_to(next);
}

void wake_up(struct wait_queue **q) {
    struct wait_queue *tmp;
    struct task_struct *p;

    if (!q || !(tmp = *q))
        return;
    do {
        p = tmp->task;
        if (p != NULL) {
            if ((p->state == TASK_UNINTERRUPTIBLE) ||
                (p->state == TASK_INTERRUPTIBLE)) {
                p->state = TASK_RUNNING;
                if (p->counter > current->counter)
                    need_resched = 1;
            }
        }
        if (!tmp->next) {
            printk("wait_queue is bad (eip = %08lx)\n", ((unsigned long *)q)[-1]);
            printk("        q = %p\n", q);
            printk("       *q = %p\n", *q);
            printk("      tmp = %p\n", tmp);
            break;
        }
        tmp = tmp->next;
    } while (tmp != *q);
}

void wake_up_interruptible(struct wait_queue **q) {
    struct wait_queue *tmp;
    struct task_struct *p;

    if (!q || !(tmp = *q))
        return;
    do {
        p = tmp->task;
        if (p != NULL) {
            if (p->state == TASK_INTERRUPTIBLE) {
                p->state = TASK_RUNNING;
                if (p->counter > current->counter)
                    need_resched = 1;
            }
        }
        if (!tmp->next) {
            printk("wait_queue is bad (eip = %08lx)\n", ((unsigned long *)q)[-1]);
            printk("        q = %p\n", q);
            printk("       *q = %p\n", *q);
            printk("      tmp = %p\n", tmp);
            break;
        }
        tmp = tmp->next;
    } while (tmp != *q);
}

static inline void __sleep_on(struct wait_queue **p, int state) {
    unsigned long flags;
    struct wait_queue wait = {current, NULL};

    if (!p)
        return;
    if (current == task[0])
        panic("task[0] trying to sleep");
    current->state = state;
    add_wait_queue(p, &wait);
    save_flags(flags);
    sti();
    schedule();
    remove_wait_queue(p, &wait);
    restore_flags(flags);
}

void interruptible_sleep_on(struct wait_queue **p) {
    __sleep_on(p, TASK_INTERRUPTIBLE);
}

void sleep_on(struct wait_queue **p) {
    __sleep_on(p, TASK_UNINTERRUPTIBLE);
}

void do_timer(struct pt_regs *regs) {
    jiffies++;
    if (regs->cs & 3)
        current->utime++;
    else
        current->stime++;

    if (current == task[0] || --current->counter <= 0) {
        current->counter = 0;
        need_resched = 1;
    }
}

void sched_init(void) {
    int i;
    struct desc_struct *p;

    set_tss_desc(gdt + FIRST_TSS_ENTRY, &(init_task.tss));
    set_ldt_desc(gdt + FIRST_LDT_ENTRY, &(init_task.ldt));
    set_system_gate(0x80, &system_call);
    p = gdt + 2 + FIRST_TSS_ENTRY;
    for (i = 1; i < NR_TASKS; i++) {
        task[i] = NULL;
        p->a = p->b = 0;
        p++;
        p->a = p->b = 0;
        p++;
    }
    __asm__("pushfl\n\tandl $0xffffbfff, (%esp)\n\tpopfl");
    load_TR(0);
    load_ldt(0);
    outb_p(0x36, 0x43);
    outb_p(LATCH & 0xff, 0x40);      // 低位字节
    outb(LATCH >> 8, 0x40);          // 高位字节
    set_intr_gate(0x20, &timer_interrupt);
    outb(inb_p(0x21) & ~0x01, 0x21); // 开启时钟中断
}
